﻿using IotClientApp.Server.Devices;
using IotClientApp.Server.Entity;
using IotClientApp.Shared;
using IotClientApp.Shared.Enums;
using IotModel;
using Microsoft.EntityFrameworkCore;
using PluginInterface;
using System.Drawing;
using System.Text;

namespace IotClientApp.Server.Services
{
    public class ParamService : IParamService
    {
        private readonly DeviceClientHost _deviceClientHost;
        private readonly IotDBContext _iotDBContext;
        private readonly ILogger<IParamService> _logger;
        public ParamService(DeviceClientHost deviceClientHost, IotDBContext iotDBContext, ILogger<IParamService> logger)
        {
            _iotDBContext = iotDBContext;
            _deviceClientHost = deviceClientHost;
            _logger = logger;
        }

        /// <summary>
        /// 获取参数
        /// </summary>
        /// <param name="input"></param>
        /// <returns></returns>
        public async Task<PageResponse<RealDataModel>> GetRealData(GetRealDataModel input)
        {
            var result = new PageResponse<RealDataModel> { };
            var deviceEntity = _iotDBContext.Device.Where(t => input.DeviceId == t.Id && t.IsActive).AsNoTracking().FirstOrDefault();
            if (deviceEntity == null)
            {
                return result;
            }
            var query = _iotDBContext.Point.Where(t => t.DeviceId == deviceEntity.Id).OrderBy(t => t.Address).AsQueryable();
            var pageCount = query.Count();
            if (input.PageSize > 0)
            {
                query = query.Skip((input.PageSize - 1) * input.PageIndex).Take(input.PageSize);
            }
            var addrs = await query.Select(t => t.Address.ToString()).AsNoTracking().ToListAsync();
            var items = _deviceClientHost.GetValues(input.DeviceId, addrs);
            result.Data = items.OrderBy(t => int.Parse(t.Address)).Select(t => new RealDataModel { Name = t.Address, Value = t.Value?.ToString(), Time = t.Timestamp }).ToList();
            return result;
        }
        /// <summary>
        /// 获取设备参数
        /// </summary>
        /// <param name="input"></param>
        /// <returns></returns>
        public async Task<PageResponse<DeviceParamModel>> GetDeviceParams(GetDeviceParamModel input)
        {
            var result = new PageResponse<DeviceParamModel> { Data = new List<DeviceParamModel> { } };
            try
            {
                var deviceEntity = _iotDBContext.Device.Where(t => input.DeviceId == t.Id).AsNoTracking().FirstOrDefault();
                if (deviceEntity == null)
                {
                    return result;
                }
                var query = _iotDBContext.Point.Where(t => t.DeviceId == deviceEntity.Id).OrderBy(t => t.Address).AsQueryable();
                result.PageCount = query.Count();
                if (input.PageSize > 0 && input.PageIndex > 0)
                {
                    query = query.Skip((input.PageIndex - 1) * input.PageSize).Take(input.PageSize);
                }
                var points = await query.ToListAsync();

                var addrs = points.Select(t => t.Address.ToString()).ToList();
                var pointValues = _deviceClientHost.GetValues(input.DeviceId, addrs);
                foreach (var point in points)
                {
                    var item = new DeviceParamModel
                    {
                        Id = point.Id,
                        Address = point.Address,
                        Name = point.Name,
                        DataType = point.DataType,
                        EndianType = point.EndianType.GetValueOrDefault(),
                        Expressions = point.Expressions ?? "",
                        Desc = point.Expressions ?? "",
                        ReadWrite = point.ReadWrite ?? "Read",
                        CreateTime = point.CreateTime,
                        DeviceId = point.DeviceId
                    };
                    switch (point.EndianType)
                    {
                        case (short)EndianEnum.BigEndian:
                            item.EndianTypeName = "大端";
                            break;
                        case (short)EndianEnum.LittleEndian:
                            item.EndianTypeName = "小端";
                            break;
                        case (short)EndianEnum.BigEndianSwap:
                            item.EndianTypeName = "大端交换";
                            break;
                        case (short)EndianEnum.LittleEndianSwap:
                            item.EndianTypeName = "小端交换";
                            break;
                        default:
                            item.EndianTypeName = "无";
                            break;
                    }

                    if (item.ReadWrite == "Read")
                    {
                        item.ReadWriteName = "可读";
                    }
                    else
                    {
                        item.ReadWriteName = "可读可写";
                    }
                    switch (point.DataType)
                    {
                        case (short)DataTypeEnum.Bit:
                            item.DataTypeName = "Bit";
                            break;
                        case (short)DataTypeEnum.Double:
                            item.DataTypeName = "Double";
                            break;
                        case (short)DataTypeEnum.Float:
                            item.DataTypeName = "Float";
                            break;
                        case (short)DataTypeEnum.Bool:
                            item.DataTypeName = "Bool";
                            break;
                        case (short)DataTypeEnum.Int16:
                            item.DataTypeName = "Int16";
                            break;
                        case (short)DataTypeEnum.Int32:
                            item.DataTypeName = "Int32";
                            break;
                    }
                    if (pointValues != null)
                    {
                        var pointValue = pointValues.FirstOrDefault(t => t.Address == point.Address.ToString());
                        if (pointValue != null)
                        {
                            if (pointValue.Value != null)
                            {
                                item.Value = pointValue.Value.ToString();
                            }
                            item.CookedValue = pointValue.CookedValue;
                            item.StatusType = pointValue.StatusType;
                            item.Timestamp = pointValue.Timestamp;
                        }
                    }
                    result.Data.Add(item);
                }
            }
            catch (Exception ex)
            {
                _logger.LogError($"GetDeviceParams {ex.Message}");
            }
            return result;
        }
        /// <summary>
        /// 保存设备参数
        /// </summary>
        /// <param name="inputs"></param>
        /// <param name="deviceId"></param>
        /// <returns></returns>
        public async Task<ApiResult<string>> SavePointInfos(List<SavePointInfoModel> inputs,string deviceId)
        {
            var result = new ApiResult<string> { };
            try
            {
                if (string.IsNullOrEmpty(deviceId))
                {
                    return result;
                }
                StringBuilder sbError = new StringBuilder();

                foreach (var input in inputs)
                {
                    var isExit = inputs.Count(t => t.Address == input.Address || t.Name == input.Name);
                    if (isExit > 1)
                    {
                        sbError.Append($"{input.Name} {input.Address}名称或者地址重复");
                    }
                }
                if (sbError.Length > 0)
                {
                    result.Code = ResultCode.Exist;
                    result.ErrorMsg = sbError.ToString();
                    return result;
                }
                
                var points = await _iotDBContext.Point.Where(t => t.DeviceId == deviceId).ToListAsync();

                foreach (var input in inputs)
                {
                    var point = points.FirstOrDefault(t => t.Address == input.Address);
                    if (point != null)
                    {
                        _iotDBContext.Attach(point);
                        _iotDBContext.Entry(point).Property(p => p.Name).IsModified = true;
                        _iotDBContext.Entry(point).Property(p => p.Address).IsModified = true;
                        _iotDBContext.Entry(point).Property(p => p.DataType).IsModified = true;
                        _iotDBContext.Entry(point).Property(p => p.EndianType).IsModified = true;
                        _iotDBContext.Entry(point).Property(p => p.Expressions).IsModified = true;
                        _iotDBContext.Entry(point).Property(p => p.Desc).IsModified = true;
                    }
                    else
                    {
                        point = new Entity.Point { DeviceId = deviceId, CreateTime = DateTime.Now, Method = "HoldingRegisters" };
                        _iotDBContext.Point.Add(point);
                    }
                    point.Name = input.Name;
                    point.Address = input.Address;
                    point.DataType = input.DataType;
                    point.EndianType = input.EndianType;
                    point.Expressions = input.Expressions;
                    point.Desc = input.Desc;
                }
                foreach (var point in points)
                {
                    var isExit = inputs.Any(t => t.Address == point.Address);
                    if (!isExit)
                    {
                        _iotDBContext.Point.Remove(point);
                    }
                }
                
                await _iotDBContext.SaveChangesAsync();
            }
            catch (Exception ex)
            {
                result.Code = ResultCode.Fail;
                _logger.LogError($"SavePointInfos{ex.Message}");
            }
            return result;
        }
        /// <summary>
        /// 参数编辑
        /// </summary>
        /// <param name="input"></param>
        /// <returns></returns>
        public async Task<ApiResult<SavePointInfoModel>> SavePointInfo(SavePointInfoModel input)
        {
            var result = new ApiResult<SavePointInfoModel> { };
            try
            {
                if (string.IsNullOrEmpty(input.DeviceId))
                {
                    result.Code = ResultCode.BadRequest;
                    return result;
                }
                var isExit = _iotDBContext.Point.Any(t => t.Id != input.Id && t.DeviceId == input.DeviceId && (t.Address == input.Address || t.Name == input.Name));
                if (isExit)
                {
                    result.Code = ResultCode.Exist;
                    result.ErrorMsg = "地址或者名称重复";
                    return result;
                }
                Entity.Point point;
                if (input.Id > 0)
                {
                    point = _iotDBContext.Point.FirstOrDefault(t => t.Id == input.Id && t.DeviceId == input.DeviceId);
                    if (point == null)
                    {
                        result.Code = ResultCode.NotExist;
                        result.ErrorMsg = "参数不存在";
                        return result;
                    }
                    _iotDBContext.Attach(point);
                    _iotDBContext.Entry(point).Property(p => p.Name).IsModified = true;
                    _iotDBContext.Entry(point).Property(p => p.Address).IsModified = true;
                    _iotDBContext.Entry(point).Property(p => p.DataType).IsModified = true;
                    _iotDBContext.Entry(point).Property(p => p.EndianType).IsModified = true;
                    _iotDBContext.Entry(point).Property(p => p.Expressions).IsModified = true;
                    _iotDBContext.Entry(point).Property(p => p.ReadWrite).IsModified = true;
                    _iotDBContext.Entry(point).Property(p => p.Desc).IsModified = true;
                }
                else
                {
                    point = new Entity.Point { DeviceId = input.DeviceId, CreateTime = DateTime.Now, Method = "HoldingRegisters" };
                    _iotDBContext.Point.Add(point);
                }
                point.Name = input.Name;
                point.Address = input.Address;
                point.DataType = input.DataType;
                point.EndianType = input.EndianType;
                point.Expressions = input.Expressions;
                point.ReadWrite = input.ReadWrite;
                point.Desc = input.Desc;
                await _iotDBContext.SaveChangesAsync();
            }
            catch (Exception ex)
            {
                result.Code = ResultCode.Fail;
                _logger.LogError($"SavePointInfo{ex.Message}");
            }
            return result;
        }
    }
}